import paho.mqtt.client as mqtt
import os
import configparser
#from loguru import logger
from top.wardyan.core.utils.mylog import *
from top.wardyan.core.vo.mqtt import MqttMsg
import uuid
from top.wardyan.core.utils.common import *
from top.wardyan.core.erp.erp import *
import time
import zlib
#import pickle
import json
import asyncio
from concurrent.futures import ThreadPoolExecutor
import threading

from top.wardyan.projects.checktool.checktool import checktool

# 创建全局线程池执行器
executor = ThreadPoolExecutor(max_workers=5)

from top.wardyan.core.utils.common import *
from top.wardyan.core.mqtt.mqttbusiness import *

## 如果有配置文件:config\dbconfig.ini，则读取配置文件
_config_file_name_ = "config\\mqttconfig.ini"
broker_address: str = ""
username: str = ""
password: str = ""
TOPIC: str = ""

_mqtt_configed_ = False
client = None

# 添加连接状态标志
_is_connected = False
# 添加重连定时器
_reconnect_timer = None
# 添加心跳定时器
_heartbeat_timer = None
# 添加重连尝试次数限制
MAX_RECONNECT_ATTEMPTS = 5
# 添加重连间隔（秒）
RECONNECT_INTERVAL = 5
# 添加心跳间隔（秒）
HEARTBEAT_INTERVAL = 30

def is_connected() :
    """检查MQTT连接状态"""
    global _is_connected, client
    if client is not None:
        return _is_connected and client.is_connected()
    return False

def send_query_sql(id : int) :
    global TOPIC
    msg: MqttMsg = MqttMsg(
        id=str(uuid.uuid4()),
        custcode=ERP_CUSTCODE,
        timestamp=current_timestamp(),
        topic=TOPIC,
        message=str(id),
        sender=ERP_CUSTCODE,
        receiver=TOPIC,
        business=10106,
        data=id
    )
    send(topic=TOPIC, message=msg.tojsonstr())

def on_message(client, userdata, msg):
    print(f"{current_timestamp()} 收到消息: {msg.payload} 从主题: {msg.topic}")
    # 初始化变量
    decompressed_data = b""
    payload_str = ""
    
    try:
        # 解压 zlib 压缩的数据
        #decompressed_data = zlib.decompress(msg.payload)
        zobj = zlib.decompressobj()
        #data = zobj.decompress(base64.b64decode(workflowDetail.origin_data))
        
        # 分段解压（即使一次性传入全部数据也适用）
        decompressed_data = zobj.decompress(msg.payload)

        # 如果还有未解压数据，则继续解压
        while zobj.unconsumed_tail:
            chunk = zobj.unconsumed_tail
            decompressed_data += zobj.decompress(chunk)
        
        # 最后刷新解压器
        decompressed_data += zobj.flush()
        # 如果还有剩余未解压数据，继续解压（通常不需要，但为了保险）
        decompressed_data += zobj.flush()

        # 尝试不同的编码方式解码
        try:
            # 首先尝试UTF-8编码
            payload_str = decompressed_data.decode('utf-8')
        except UnicodeDecodeError:
            try:
                # 如果UTF-8失败，尝试GBK编码（处理中文字符）
                payload_str = decompressed_data.decode('gbk')
            except UnicodeDecodeError:
                # 如果都失败，使用UTF-8的错误处理模式
                payload_str = decompressed_data.decode('utf-8', errors='ignore')
                
        #print("长度:", len(msg.payload) / 1024, "KB")
        print(f"{current_timestamp()} 收到消息: {payload_str} 从主题: {msg.topic} 长度:{len(msg.payload) / 1024:.2f}kb")
        ## 检查消息内容并加以处理
        ## 将payload_str转为json
        payload_dict = json.loads(payload_str)
        business = payload_dict.get("business", 0)
        # if business == 10101 :
        #     handle_10101(payload_dict)
        if business in BUSINESSES :
            BUSINESSES[business](payload_dict)
        else :
            print(f"未知业务: {business}")

    except zlib.error as e:
        print(f"zlib 解压失败: {e}，原始数据为: {msg.payload}")
    except UnicodeDecodeError:
        # decompressed_data已经在上面定义并赋值
        print(f"解压成功，但无法解码为 UTF-8 字符串。原始解压数据为: {decompressed_data}")
    except json.JSONDecodeError as e:
        print(f"JSON解析失败: {e}，消息内容为: {payload_str}")

# 定义连接回调函数

def on_connect(client, userdata, flags, rc):
    global _is_connected
    if rc == 0:
        _is_connected = True
        topic = "checktoolclient_" + ERP_CUSTCODE
        client.subscribe(topic)
        print("成功连接到MQTT服务器:{},监听主题:{}", broker_address, topic)
        logger.info("成功连接到MQTT服务器:{},监听主题:{}", broker_address, topic)
        # 连接成功后启动心跳
        start_heartbeat()
    else:
        _is_connected = False
        print(f"连接失败，返回码: {rc}")
        logger.error(f"连接MQTT服务器失败，返回码: {rc}")

def on_disconnect(client, userdata, rc):
    """MQTT断开连接回调"""
    global _is_connected, _reconnect_timer
    _is_connected = False
    logger.warning("与MQTT服务器断开连接，返回码: {}", rc)
    # 取消之前的重连定时器（如果存在）
    if _reconnect_timer is not None:
        _reconnect_timer.cancel()
    # 启动定时重连
    schedule_reconnect()

def _connect_mqtt() :
    global client
    global _mqtt_configed_
    global _is_connected
    global on_connect
    global _config_file_name_
    global broker_address
    global TOPIC
    if os.path.exists(_config_file_name_) :
        ## 读取配置文件
        config = configparser.ConfigParser()
        config.read(_config_file_name_)
        broker_address = config.get("mqtt", "broker_address")
        username = config.get("mqtt", "username")
        password = config.get("mqtt", "password")
        TOPIC = config.get("mqtt", "topic")
        
        try :
            # 创建MQTT客户端实例
            client = mqtt.Client()
            # 设置用户名和密码
            client.username_pw_set(username, password)
            
            # 将连接回调函数分配给客户端
            client.on_connect = on_connect
            # 将回调绑定到客户端
            client.on_disconnect = on_disconnect
            
            client.on_message = on_message
            # 连接到MQTT代理，设置心跳间隔
            client.connect(broker_address, keepalive=HEARTBEAT_INTERVAL*2)
            client.loop_start()  # 启动后台网络循环
            
            _mqtt_configed_ = True
            # 初始化连接状态
            _is_connected = True

        except BaseException as e :
            _is_connected = False
            logger.error("连接MQTT服务器失败:{}", e)
            
        ##print(f"{current_timestamp()} 连接到MQTT: broker_address={broker_address}, username={username}, password={password}, topic={TOPIC}")
        logger.info(f"{current_timestamp()} 连接到MQTT: broker_address={broker_address}, username={username}, password={password}, topic={TOPIC}")
    else :
        print(f"未找到配置文件: {_config_file_name_}")

def reconnect_mqtt():
    """尝试重新连接MQTT服务器"""
    global client, _is_connected, broker_address, username, password
    try:
        if client is not None:
            logger.info("尝试重新连接MQTT服务器...")
            client.reconnect()
            _is_connected = True
            logger.info("MQTT重新连接成功")
            return True
    except Exception as e:
        _is_connected = False
        logger.error(f"MQTT重连失败: {e}")
        return False

def schedule_reconnect():
    """安排重连任务"""
    global _reconnect_timer
    logger.info("安排MQTT重连任务，{}秒后尝试重连", RECONNECT_INTERVAL)
    _reconnect_timer = threading.Timer(RECONNECT_INTERVAL, try_reconnect_with_backoff)
    _reconnect_timer.daemon = True
    _reconnect_timer.start()

def try_reconnect_with_backoff():
    """带退避策略的重连尝试"""
    global _reconnect_timer
    for attempt in range(MAX_RECONNECT_ATTEMPTS):
        logger.info("MQTT重连尝试 {}/{}", attempt + 1, MAX_RECONNECT_ATTEMPTS)
        if reconnect_mqtt():
            return  # 重连成功，退出
        if attempt < MAX_RECONNECT_ATTEMPTS - 1:
            # 等待一段时间再重试
            time.sleep(RECONNECT_INTERVAL * (attempt + 1))  # 递增等待时间
    
    # 所有重试都失败了，安排下次重连
    logger.error("MQTT重连所有尝试都失败，将在{}秒后再次尝试", RECONNECT_INTERVAL)
    _reconnect_timer = threading.Timer(RECONNECT_INTERVAL, try_reconnect_with_backoff)
    _reconnect_timer.daemon = True
    _reconnect_timer.start()

def ensure_connection():
    """确保MQTT连接有效，必要时尝试重连"""
    if not is_connected():
        logger.warning("MQTT连接已断开，尝试重新连接...")
        # 先尝试立即重连一次
        if not reconnect_mqtt():
            # 如果立即重连失败，安排后续重连
            schedule_reconnect()
            return False
    return True

def send(topic="", message="") :
    global client
    global _mqtt_configed_
    global TOPIC
    if topic is None or topic == "" :
        topic = TOPIC
    if _mqtt_configed_ and message is not None:
        # 发送前检查连接状态
        if not ensure_connection():
            logger.error("MQTT连接不可用，无法发送消息")
            return False
            
        # 如果message 是MqttMsg类型，则转换为json字符串,是字符串直接发出,否则转换为字符串直接发出
        try :
            msg_content = ""
            if isinstance(message, MqttMsg) :
                if message.topic is None or message.topic == "" :
                    message.topic = topic
                if message.receiver is None or message.receiver == "" :
                    message.receiver = TOPIC
                if message.sender is None or message.sender == "" :
                    message.sender = "checktoolclient_" + ERP_CUSTCODE
                msg_content = message.tojsonstr()            
            else:
                msg_content = str(message)
                
            # time.sleep(0.1)
            # message = zlib.compress(message)
            msg = msg_content
            compressed_message = zlib.compress(msg_content.encode('utf-8'))
            ##client.on_connect_fail()
            if client is not None:
                result = client.publish(topic, compressed_message)
                ## client.disconnect()
                #print("成功: 主题: {} 发布消息: {} 到MQTT服务器: {}".format(topic, message, broker_address))
                logger.info("发布消息到MQTT成功: 主题: {}, 消息长度(原始):{:.2f},消息长度(压缩后):{}, 发布消息: {}, 到MQTT服务器: {}".format(topic,len(msg)/1024, len(compressed_message), msg, broker_address))
                return result.rc == mqtt.MQTT_ERR_SUCCESS
            else:
                logger.error("MQTT客户端未初始化")
                return False
        except Exception as e :
            logger.error(e)
            return False
    else :
        # print("未配置MQTT服务器")
        logger.info("未配置MQTT服务器或消息为空")
        return False

## 心跳第几轮了
round : int = 1

def start_heartbeat():
    """启动心跳定时器"""
    global _heartbeat_timer
    # 取消之前的心跳定时器（如果存在）
    if _heartbeat_timer is not None:
        _heartbeat_timer.cancel()
    
    # 启动新的心跳定时器
    _heartbeat_timer = threading.Timer(HEARTBEAT_INTERVAL, heartbeat_task)
    _heartbeat_timer.daemon = True
    _heartbeat_timer.start()

def heartbeat_task():
    """心跳任务"""
    global round, _heartbeat_timer
    try:
        # 发送心跳消息
        mqtt_heartbeat()
    except BaseException as e:
        logger.error("MQTT发送心跳失败:{}", e)
    finally:
        round += 1
        # 安排下一次心跳
        _heartbeat_timer = threading.Timer(HEARTBEAT_INTERVAL, heartbeat_task)
        _heartbeat_timer.daemon = True
        _heartbeat_timer.start()

def mqtt_heartbeat(topic : str = TOPIC) :
    global round
    try :
        ## 每一百伦就重新发一次ERP版本和客户端版本
        data = {
            "round" : round
        }
        msg: MqttMsg = MqttMsg(
            id=str(uuid.uuid4()),
            custcode=ERP_CUSTCODE,
            timestamp=current_timestamp(),
            topic=topic,
            message=json.dumps(data),
            sender=ERP_CUSTCODE,
            receiver=topic,
            business=10101,
            data=data
        )
        
        send(topic, msg.tojsonstr())
    except BaseException as e :
        logger.error("MQTT发送心跳失败:{}", e)
        
    round += 1

# 初始化MQTT连接
_connect_mqtt()